package com.nearsoft.academy;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.hardware.*;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Vibrator;
import android.util.Log;
import android.view.*;
import android.widget.Button;
import android.widget.TextView;

import java.io.File;
import java.io.FileOutputStream;
import java.util.List;

public class MainActivity extends Activity {
    SurfaceView cameraPreview;
    SurfaceHolder previewHolder;
    Camera camera;
    Vibrator vibrator;
    boolean inPreview;

    int photoCounter = 0;

    final static String TAG = "MainActivity";
    protected static final int SAMPLE_FREQUENCY = 4;
    SensorManager sensorManager;

    private PathTest activeTest = new PathTest();

    private TextView orientationValue;
    private VisualAid visualAid;
    private Button toggleStart;

    protected int sampleCounter;

    float[] rotationMatrix;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        vibrator = (Vibrator)getSystemService(VIBRATOR_SERVICE);

        Sensor rotation = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
        sensorManager.registerListener(sensorEventListener, rotation, SensorManager.SENSOR_DELAY_UI);

        inPreview = false;

        cameraPreview = (SurfaceView) findViewById(R.id.cameraPreview);
        previewHolder = cameraPreview.getHolder();
        previewHolder.addCallback(surfaceCallback);
        previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        orientationValue = (TextView) findViewById(R.id.orientationValue);
        visualAid = (VisualAid) this.findViewById(R.id.visualAid);
        toggleStart = (Button) this.findViewById(R.id.start);

        initTest();
        showDebugInfo(false);

        toggleStart.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                if (!activeTest.isActive()){
                    startTest();
                    takePicture();
                }
                else {
                    stopTest();
                }
            }
        });


    }

    public void deleteDB() {
        LogDatabaseHandler logDB = new LogDatabaseHandler(this);
        logDB.deleteAllData();
    }

    void clearData() {
        AlertDialog.Builder alert = new AlertDialog.Builder(this);
        alert.setTitle("You suuuuure??");
        boolean result = false;
        alert.setPositiveButton("Totally!", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                MainActivity.this.deleteDB();
            }
        });

        alert.setNegativeButton("No, jk", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                // Do nothing, just cancel
            }
        });

        alert.show();
    }

    private void initTest(){
        LogDatabaseHandler logDB = new LogDatabaseHandler(this);
        List<String> logNames = logDB.getLogNames();
        activeTest = new PathTest("Test " + String.valueOf(logNames.size() + 1));

        // Instantiate and initialize to identity
        rotationMatrix = new float[9];
        rotationMatrix[ 0] = 1;
        rotationMatrix[ 4] = 1;
        rotationMatrix[ 8] = 1;

        sampleCounter = 0;
    }

    void startTest() {
        initTest();
        //activeTest.setActive(true);
        visualAid.startCapture();
        toggleStart.setText(getText(R.string.stop_test));


    }

    void stopTest() {
        if (activeTest.isActive()) {
            toggleStart.setText(getText(R.string.start_test));

            activeTest.setActive(false);
            visualAid.stopCapture();

            AlertDialog.Builder alert = new AlertDialog.Builder(this);
            alert.setTitle("Save test?");

            alert.setPositiveButton("Yes :)", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                    saveTestLogToDB();
                }
            });

            alert.setNegativeButton("No :(", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                    // Do nothing, just cancel
                }
            });

            alert.show();
        }
    }

    private void saveTestLogToDB(){
        LogDatabaseHandler logDB = new LogDatabaseHandler(this);
        logDB.addOrientationDataList(activeTest.getName(), activeTest.getOrientationDataList());
    }

    private void saveTestLogsToSD(){
        LogDatabaseHandler logDB = new LogDatabaseHandler(this);
        logDB.saveToSD();
    }

    private boolean needsSample() {
        sampleCounter++;
        return sampleCounter % SAMPLE_FREQUENCY == 0;
    }

    private float[] calculateOrientation(float[] rotationVectorValues) {
        SensorManager.getRotationMatrixFromVector(rotationMatrix, rotationVectorValues);
        float[] outR = rotationMatrix.clone();
        float[] orientationValues = new float[3];
        SensorManager.getOrientation(outR, orientationValues);

        orientationValues[0] = (float) Math.toDegrees(orientationValues[0]);
        orientationValues[1] = (float) Math.toDegrees(orientationValues[1]);
        orientationValues[2] = (float) Math.toDegrees(orientationValues[2]);

        return orientationValues;
    }

    private void updateVisualAid (float[] orientationValues){
        if (visualAid != null){
            visualAid.setAzimuth(orientationValues[0]);
            visualAid.setPitch(orientationValues[1]);
            visualAid.setRoll(orientationValues[2]);
            visualAid.invalidate();
            if (visualAid.isPhotoTime()) {
                takePicture();
                visualAid.setPhotoTime(false);
            }
        }
    }

    private void updateDebugInfo(float[] orientationValues){
        if (orientationValue != null) {
            StringBuilder data = new StringBuilder();
            data.append("azimuth: "); data.append(orientationValues[0]); data.append("\n");
            data.append("pitch  : "); data.append(orientationValues[1]); data.append("\n");
            data.append("roll   : "); data.append(orientationValues[2]); data.append("\n");
            data.append("diff   : "); data.append(visualAid.getAzimuthDifference()); data.append("\n");
            orientationValue.setText(data.toString());
        }
    }

    private void logOrientation(float[] values) {
        activeTest.addOrientationEvent(values[0], values[1], values[2]);
    }

    final SensorEventListener sensorEventListener = new SensorEventListener() {

        public void onSensorChanged(SensorEvent sensorEvent) {

            if (sensorEvent.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR){
                float[] orientationValues = calculateOrientation(sensorEvent.values);

                updateVisualAid(orientationValues);
                updateDebugInfo(orientationValues);
                if (activeTest.isActive() && needsSample()){
                    logOrientation(orientationValues);
                }
            }
        }

        public void onAccuracyChanged(Sensor sensor, int accuracy) {
            // Not used
        }
    };

    @Override
    public void onResume() {
        super.onResume();

        sensorManager.registerListener(sensorEventListener, sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR), SensorManager.SENSOR_DELAY_UI);
        camera = Camera.open();
    }

    @Override
    public void onPause() {
        if (inPreview) {
            camera.stopPreview();
        }

        sensorManager.unregisterListener(sensorEventListener);
        camera.release();
        camera = null;
        inPreview = false;

        super.onPause();
    }

    private Camera.Size getBestPreviewSize(int width, int height, Camera.Parameters parameters) {
        Camera.Size result = null;

        for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
            if (size.width <= width && size.height <= height) {
                if (result == null) {
                    result = size;
                } else {
                    int resultArea = result.width * result.height;
                    int newArea = size.width * size.height;

                    if (newArea > resultArea) {
                        result = size;
                    }
                }
            }
        }

        return (result);
    }

    SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {

        public void surfaceCreated(SurfaceHolder holder) {
            try {
                camera.setPreviewDisplay(previewHolder);
            } catch (Throwable t) {
                Log.e(TAG, "Exception in setPreviewDisplay()", t);
            }
        }

        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            if (camera == null) {
                return;
            }
            Camera.Parameters parameters = camera.getParameters();
            Camera.Size size = getBestPreviewSize(width, height, parameters);

            if (size != null) {
                parameters.setPreviewSize(size.width, size.height);
                camera.setParameters(parameters);
                camera.startPreview();
                inPreview = true;
            }
        }

        public void surfaceDestroyed(SurfaceHolder holder) {
            // not used
        }
    };

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.clear_data:
                clearData();
            case R.id.show_debug_info:
                item.setChecked(!item.isChecked());
                showDebugInfo(item.isChecked());
            case R.id.save_data:
                saveTestLogsToSD();
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    private void showDebugInfo(boolean checked) {
        if (!checked) {
            Log.i(TAG, "debug info");
            orientationValue.setVisibility(View.GONE);
        } else {
            Log.i(TAG, "Showing debug info");
            orientationValue.setVisibility(View.VISIBLE);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }



    void takePicture() {
        camera.autoFocus(new Camera.AutoFocusCallback() {
            Camera.ShutterCallback shutterCallback = new Camera.ShutterCallback() {
                public void onShutter() {
                    vibrator.vibrate(300);
                }
            };
            public void onAutoFocus(boolean success, Camera camera) {
                if (success) {
                    camera.takePicture(shutterCallback, null, photoCallback);
                }
            }
        });
    }

    Camera.PictureCallback photoCallback=new Camera.PictureCallback() {
        public void onPictureTaken(byte[] data, Camera camera) {
            new SavePhotoTask(photoCounter).execute(data);
            photoCounter++;
            camera.startPreview();
            inPreview=true;
        }
    };

    class SavePhotoTask extends AsyncTask<byte[], String, String> {
        int photoCounter;

        public SavePhotoTask(int photoCounter) {
            this.photoCounter = photoCounter;
        }

        @Override
        protected String doInBackground(byte[]... jpeg) {
            File photo=
                    new File(Environment.getExternalStorageDirectory(),
                            "/PersonalData/photo" + String.valueOf(this.photoCounter) + ".jpg");
            if (photo.exists()) {
                photo.delete();
            }

            try {
                FileOutputStream fos=new FileOutputStream(photo.getPath());

                fos.write(jpeg[0]);
                fos.close();
            }
            catch (java.io.IOException e) {
                Log.e("PictureDemo", "Exception in photoCallback", e);
            }

            return(null);
        }
    }
}
